package vn.mahjonggame.game.request;

import java.util.Collections;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.log4j.Logger;

import vn.mahjonggame.game.Mahjong;
import vn.mahjonggame.game.MahjongExtension;
import vn.mahjonggame.metadata.GameActionType;
import vn.mahjonggame.metadata.MatchState;
import vn.mahjonggame.metadata.Player;
import vn.mahjonggame.metadata.MObject;
import vn.mahjonggame.metadata.Response;
import vn.mahjonggame.metadata.RoomVarName;
import vn.mahjonggame.metadata.Tile;
import vn.mahjonggame.metadata.TileSet;

import com.smartfoxserver.v2.entities.Room;
import com.smartfoxserver.v2.entities.User;
import com.smartfoxserver.v2.entities.data.ISFSObject;
import com.smartfoxserver.v2.entities.data.SFSArray;
import com.smartfoxserver.v2.entities.data.SFSObject;
import com.smartfoxserver.v2.extensions.BaseClientRequestHandler;

public class CallKanHandler extends BaseClientRequestHandler {

	@Override
	public void handleClientRequest(final User user, ISFSObject params) {
		// TODO Auto-generated method stub
		final MahjongExtension ext = (MahjongExtension) getParentExtension();		
		callKan(ext, user, params);
	}
	
	public void callKan(final MahjongExtension ext, final User user, ISFSObject params){		
		int kanTileId = params.getInt(MObject.tile.toString());
		String kanType = params.getUtfString(MObject.kanType.toString());
		int bonusTime = params.getInt(MObject.bonusTime.toString());		
				
		Mahjong mahjong = ext.getMahjong();
		List<User> users = user.getLastJoinedRoom().getUserList();
		Room room = ext.getGameRoom();
		Logger logger = mahjong.getLogger();
		
		List<Player> players = mahjong.getPlayers();
		
		final Player player = mahjong.getPlayerById(user.getId());
		player.cancelTimer();
		
		Player thrownPlayer = mahjong.getThrownPlayer();
		
		List<Tile> playerRightTiles = player.getRightTiles();
		List<Tile> playerLeftTiles = player.getLeftTiles();			
				
		Tile kanTile = mahjong.getTileById(kanTileId);
				
		logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "kanType: " + kanType));
		logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "tile: " + kanTile.toString()));
		
		/*
		 * Check if the mahjong is HOA, then show the result
		 */		
		int numPickedDeathTile = mahjong.getPickedDeathTile().size();
		//int playerNumPickedDeathTile = player.getPickedDeathTile().size();
		int numCalledKanPlayers = mahjong.getCalledKanPlayers().size();
		
		mahjong.getCalledKanPlayers().add(player);
		
		logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "PickedDeathTile: " + mahjong.getPickedDeathTile()));
		logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "CalledKanPlayers: " + mahjong.getCalledKanPlayers()));
		
		/*
		 * Check if another player can call RON
		 */
		mahjong.setCanCallOpenKanPlayer(null);
		if(mahjong.getCanCallRonPlayers().size() > 0){
						
			mahjong.setCallingOpenKanPlayer(player);				
			
			/*
			 * Cache some info to be used in case the RON is ignored
			 */
			Map playerCache = player.getCache();			
			playerCache.put(MObject.tile.toString(), kanTileId);
			playerCache.put(MObject.kanType.toString(), kanType);
			playerCache.put(MObject.bonusTime.toString(), bonusTime);
			
			send(GameActionType.waitting.toString(), new SFSObject(), user);
			
			logger.info(mahjong.log(room, player, GameActionType.callChi.toString(), "Wait for another RON..."));
			return;
		}
				
		Response.Builder response = new Response.Builder();
		Response.Builder object = new Response.Builder();		
		
		if(numPickedDeathTile == 3 && numCalledKanPlayers > 2 && mahjong.getCanCallRonPlayers().size() == 0){
			
			
			SFSArray sfsPlayerArray = new SFSArray();
			
			for(int i = 0; i < players.size(); i++){
				
				Player iPlayer = players.get(i);
				
				Response.Builder sfsUser = new Response.Builder();
				sfsUser.name(iPlayer.getName())
						.point(0)
						.money(0)
						.isWinner(false)
						.tiles(mahjong.tileArrayToTileIdArray(iPlayer.getLeftTiles()));
									
				logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "Player " + iPlayer.toString() + " " + iPlayer.getLeftTiles()));
				sfsPlayerArray.addSFSObject(sfsUser.build().getSFSObject());
			}
			
			object.type(1)
					.winPos(player.getPlayerPosition());			
			
			response.HOA(object.build().getSFSObject())
					.players(sfsPlayerArray);
			
			send(TileSet.HOA.toString(), response.build().getSFSObject(), users);			
			logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "Mahjong is HOA"));
			ext.setMatchState(MatchState.SHOW_RESULT);
			return;
		}
		
		if(kanType.equalsIgnoreCase(TileSet.OPENKAN.toString())){					
			Tile tile = thrownPlayer.getJustThrownTile();	
			
			if(!kanTile.equals(tile)){
				String msg = "The open kan tile ("+tile.getId()+") is wrong. The correct is " + kanTile.getId();
				logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), msg));
				SFSObject obj  = mahjong.makeResponseMessage(msg);
				send(GameActionType.callKan.toString(), obj, user);
				return;
			}			
		}
		else {
			Tile tile = player.getJustPickedTile();
			
			if(!kanTile.equals(tile)){
				String msg = "The close/late kan tile ("+tile.getId()+") is wrong. The correct is " + kanTile.getId();
				logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), msg));
				SFSObject obj  = mahjong.makeResponseMessage(msg);
				send(GameActionType.callKan.toString(), obj, user);
				return;
			}
		}
				
		playerRightTiles.add(kanTile);
		playerLeftTiles.removeAll(Collections.singleton(kanTile));			
		
		/*
		 * Add the CHI SET to the right tile list and remove the correspond tile on the left tiles 
		 */
		if(!kanType.equalsIgnoreCase(TileSet.LATEKAN.toString())){
			
			playerRightTiles.addAll(Arrays.asList(kanTile, kanTile, kanTile));
		}		

		logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "leftTiles: " + player.getLeftTiles()));	
		logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "rightTiles: " + player.getRightTiles()));		
		
		logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "tileSet: " + player.logTileSet()));		
		/*
		 * The tile of player is become public
		 */
		player.setPublic(true);
		
		/*
		 * increase the numTimesCallKan, if CLOSEKAN, ignore it 
		 */
		if(!kanType.equalsIgnoreCase(TileSet.CLOSEKAN.toString())){
			int numTimesCallKan = player.getNumTimesCallKan();				
			numTimesCallKan++;
			player.setNumTimesCallKan(numTimesCallKan);
		}		
		
		/*
		 * Response
		 */		
		int tileId = kanTile.getId();		
				
		object.losePos(thrownPlayer != null ? thrownPlayer.getPlayerPosition() : -1)
				.winPos(player.getPlayerPosition())
				.loseTile(tileId);
		
		if(kanType.equalsIgnoreCase(TileSet.LATEKAN.toString())){
			object.winTile(Arrays.asList(tileId));			
		}
		else {
			object.winTile(Arrays.asList(tileId, tileId, tileId, tileId));			
		}
		
		response.turn(player.getPlayerPosition())
				.turnTime(player.getTurnTime()/1000)
				.bonusTime(player.getBonusTime()/1000);
				
		if(kanType.equalsIgnoreCase(TileSet.LATEKAN.toString())){
			response.LATEKAN(object.build().getSFSObject());
		}
		else if(kanType.equalsIgnoreCase(TileSet.OPENKAN.toString())){
			response.OPENKAN(object.build().getSFSObject());
		}
		else {
			response.CLOSEKAN(object.build().getSFSObject());
		}
		
		/*
		 * Send skip for the waiting player
		 */
		Player callingChiPlayer = mahjong.getCallingChiPlayer();
		if(callingChiPlayer != null){			
			mahjong.setCallingChiPlayer(null);			
			
			final User tmpUser = mahjong.getUserByPlayerId(callingChiPlayer.getPlayerId(), users);
						
			final Response.Builder nextParams = new Response.Builder();
			nextParams.type(TileSet.CHI.toString())
						.isConflict(true)
						.turn(callingChiPlayer.getPlayerPosition());
			
			send(GameActionType.skip.toString(), nextParams.build().getSFSObject(), tmpUser);
			
			logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "Send skip cmd for :" + player.toString()));
		}
		
		send(GameActionType.callKan.toString(), response.build().getSFSObject(), users);
		
		/*
		 * Turn on the KAN/CHI/PON call flag
		 */
		Map<Integer, Boolean> numKCPCallDuringRiichiMap = mahjong.getNumKCPCallDuringRiichiMap();
		
		for(Iterator<Integer> iPlayerIds = numKCPCallDuringRiichiMap.keySet().iterator(); iPlayerIds.hasNext();){
			int iPlayerId = iPlayerIds.next();
			numKCPCallDuringRiichiMap.put(iPlayerId, true);
		}
		
		ext.addAction(user, GameActionType.callKan.toString());
		
		player.setHasPicked(false);		
		
		player.updateBonusTime();
		
		logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "Bonus time from client: " + bonusTime * 1000));			
		logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "Bonus time from server: " + player.getBonusTime()));
		logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "startTime: " + player.getStartTime()));
		logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "leftTime: " + (player.getEndTime() - player.getStartTime())));
		
		mahjong.setPlayerInTurn(player);
		mahjong.resetTileSet();
		
		if(!kanType.equalsIgnoreCase(TileSet.OPENKAN.toString())
				&& (player.isDisconnect() || Boolean.valueOf(room.getVariable(RoomVarName.is_auto.toString()).getStringValue()))){
			/*
			 * Wait 5 second. If player does not thrown the tile, auto thrown the tile
			 */			
			final Response.Builder nextParams = new Response.Builder();			
			int size = player.getLeftTiles().size();
			final Tile tile = player.getLeftTiles().get(size - 1);
			nextParams.tile(tile.getId());			
			
			int delay = player.getTurnTime() + player.getBonusTime();	
			/*
			 * Only wait 1 second if the player is disconnected
			 */
			if(player.isDisconnect()){
				delay = 1000;
			}
			logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "isDisconnect: " + player.isDisconnect()));			
			logger.info(mahjong.log(room, player, GameActionType.callKan.toString(), "Calling throw handler: " + delay));
			Timer timer = player.getTimer();
			timer.schedule(new TimerTask() {
				  @Override
				  public void run() {
					  if(!player.isHasThrown()){
						  ThrowTileHandler throwTileHandler = new ThrowTileHandler();					  
						  throwTileHandler.throwTile(ext, user, nextParams.build().getSFSObject(), true, true);
					  }								
				  }
			}, delay);
		}
	}
}
